#include "Triangle.h"

#include "GeomException.h"
#include "Ray.h"
#include <math.h>
using namespace geom;

Vector3D Triangle::getNormal() const
{
    Vector3D v1(p2.x-p1.x, p2.y-p1.y, p2.z-p1.z);
    Vector3D v2(p3.x-p1.x, p3.y-p1.y, p3.z-p1.z);
    return v2.vectorProduct(v1).getNormalized();
}

Triangle::Triangle()
{
    Point3D p(0,0,0);
    Vector3D n(0,0,0);
    p1=p2=p3 = p;
//    n1=n2=n3 = n;
//    normalsCreated[0] = normalsCreated[1] = normalsCreated[2] = false;
}

Triangle::Triangle(const Point3D &tp1, const Point3D &tp2, const Point3D &tp3) : p1(tp1), p2(tp2), p3(tp3)
{
//    Vector3D n0(0,0,0);
 //   n1=n2=n3 = n0;
//    normalsCreated[0] = normalsCreated[1] = normalsCreated[2] = false;
}

Triangle::Triangle(const util::Triplet<MeshPoint> tri)
{
	for(int i=0;i<3;++i)
		setPoint(tri[i].p,i);
}


bool Triangle::hasPoint(const Point3D &p) const
{
    return (p1 == p || p2 == p || p3 == p);
}

void Triangle::setPoint(const Point3D &p, int pointNum)// Points number: 0 to 2
{
    switch(pointNum)
    {
        case 0:
                p1 = p;
                break;
        case 1:
                p2 = p;
                break;
        case 2:
                p3 = p;
                break;
        default:
                throw GeomException("Triangles have only 3 vertices: 0, 1, 2");
    };
}

/*
void Triangle::setNormal(const Vector3D &n, int pointNum)// Points number: 0 to 2
{
    switch(pointNum)
    {
        case 0:
                n1 = n;
                break;
        case 1:
                n2 = n;
                break;
        case 2:
                n3 = n;
                break;
        default:
                throw GeomException("Triangles have only 3 vertices: 0, 1, 2");
    };
}
*/

Point3D Triangle::getPoint(int pointNum) const
{
    switch(pointNum)
    {
        case 0: return p1;
        case 1: return p2;
        case 2: return p3;
        default:
                throw GeomException("Triangles have only 3 vertices: 0, 1, 2");
    };
}

/*
Vector3D Triangle::getNormal(int pointNum) const
{
    switch(pointNum)
    {
        case 0: return n1;
        case 1: return n2;
        case 2: return n3;
        default:
                throw GeomException("Triangles have only 3 vertices: 0, 1, 2");
    };
}
*/

bool Triangle::isAdyacentTo(const Triangle &t) const
{
    if(p1 == t.p1 ||  p1 == t.p2 || p1 == t.p3)
        return true; 

    if(p2 == t.p1 ||  p2 == t.p2 || p2 == t.p3)
        return true; 

    if(p3 == t.p1 ||  p3 == t.p2 || p3 == t.p3)
        return true; 

    return false;
}

bool Triangle::isIntersectedBy(const Ray &r) const
{   
	Point3D p;
	int ret = getRayIntersectionPoint(r,&p) >=1;
	return ret>=1;
}


#define SMALL_NUM  0.00000001f // anything that avoids division overflow

// intersect_RayTriangle(): intersect a ray with a 3D triangle
//    Input:  a ray R, and a triangle T
//    Output: *I = intersection point (when it exists)
//    Return: -1 = triangle is degenerate (a segment or point)
//             0 = disjoint (no intersect)
//             1 = intersect in unique point I1
//             2 = are in the same plane

int Triangle::getRayIntersectionPoint(const Ray &R, Point3D* result ) const
{
    // TAKEN FROM THE BOOK: Physically Based Rendering, by Matt Pharr

    // Compute $\VEC{s}_1$
    // Get triangle vertices in _p1_, _p2_, and _p3_
    Vector3D e1 = p2 - p1;
    Vector3D e2 = p3 - p1;
    Vector3D s1 = R.direction().vectorProduct(e2);
    float divisor = s1.dotProduct(e1);
    if (divisor == 0.)
        return 2;
    float invDivisor = 1.f / divisor;
    // Compute first barycentric coordinate
    Vector3D d = R.origin() - p1;
    float b1 = d.dotProduct(s1) * invDivisor;
    if (b1 < 0. || b1 > 1.)
        return 0;
    // Compute second barycentric coordinate
    Vector3D s2 = d.vectorProduct(e1);
    float b2 = R.direction().dotProduct(s2) * invDivisor;
    if (b2 < 0. || b1 + b2 > 1.)
        return 0;
    // Compute _t_ to intersection point
    float t = e2.dotProduct(s2) * invDivisor;
    if (t < 0 )//|| t > R.maxT)
        return 0;
    *result = R.origin() + R.direction()*t;
    return 1;
}




